﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Research1
{
    public partial class Form1 : Form
    {
        int octaves = 1;

        struct Vector
        {
            private static Random generator = new Random();

            public override string ToString()
            {
                return "[" + x + "," + y + "]";
            }
            public Vector(float X, float Y, float Z)
            {
                x = X;
                y = Y;
                z = Z;
            }

            public static Vector operator -(Vector a, Vector b)
            {
                return new Vector(a.x - b.x, a.y - b.y, a.z - b.z);
            }


            public static Vector Random()
            {
                Vector v = new Vector(((float)generator.NextDouble() - 0.5f) * 2, ((float)generator.NextDouble() - 0.5f) * 2, ((float)generator.NextDouble() - 0.5f) * 2);
                if (v.x == 0f && v.y == 0f && v.z == 0f)
                {
                    v.x = generator.Next(2) > 1 ? -0.5f : 0.5f;
                    v.y = generator.Next(2) > 1 ? -0.5f : 0.5f;
                    v.z = generator.Next(2) > 1 ? -0.5f : 0.5f;
                }

                return v;
            }

            public static void Normalize(ref Vector v)
            {
                float l = (float)Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
                if (l == 0.0f)
                    v.x = v.y = v.z = 0.0f;
                else
                {
                    v.x /= l;
                    v.y /= l;
                    v.z /= l;
                }
            }

            public static float Dot(ref Vector a, ref Vector b)
            {
                return (a.x * b.x + a.y * b.y + a.z * b.z);
            }

            public float x;
            public float y;
            public float z;
        }

        const int NOISE_DIM = 256;
        const int BASE_DATA_DIM = 8;

        Vector[,,] basedata = new Vector[BASE_DATA_DIM,BASE_DATA_DIM,BASE_DATA_DIM];

        public Form1()
        {
            InitializeComponent();
            InitNoise();
            GenerateNoise();


            Refresh();
        }



        Vector QueryBase(int x, int y, int z)
        {
            int xi = (x % BASE_DATA_DIM);
            int yi = (y % BASE_DATA_DIM);
            int zi = (z % BASE_DATA_DIM);
            
            return basedata[xi,yi,zi];
        }


        void InitNoise()
        {
            for (int x = 0; x < BASE_DATA_DIM ; x++)
            {
                for (int y = 0; y < BASE_DATA_DIM ; y++)
                {
                    for (int z = 0; z < BASE_DATA_DIM; z++)
                    {
                        Vector v = Vector.Random();
                        Vector.Normalize(ref v);
                        basedata[x,y,z] = v;
                    }
                }
            }
        }

        float InterpolateCurve(float s)
        {
            s = 1f - s;
            return s * s * s * (s * (s * 6.0f - 15.0f) + 10.0f);
        }
        float InterpolateLinear(float a, float b, float t)
        {
            return (a + (1f - t) * (b - a));
        }

        int Alpha(float a)
        {
            if (a > 1.0f)
                return 255;
            if (a < 0f)
                return 0;
            return (int)(a * 255f);
        }

        float Noise(Vector coordinate, Graphics g)
        {
            float x = coordinate.x - (float)Math.Floor(coordinate.x);
            float y = coordinate.y - (float)Math.Floor(coordinate.y);
            float z = coordinate.z - (float)Math.Floor(coordinate.z);

            int q_leftc = (int)(x * BASE_DATA_DIM);
            int q_rightc = (q_leftc + 1);
            int q_topc = (int)(y * BASE_DATA_DIM);
            int q_bottomc = (q_topc + 1);
            int q_frontc = (int)(z * BASE_DATA_DIM);
            int q_backc = (q_frontc + 1);

            Vector q_tlf = QueryBase(q_leftc, q_topc, q_frontc);
            Vector q_trf = QueryBase(q_rightc, q_topc, q_frontc);
            Vector q_blf = QueryBase(q_leftc, q_bottomc, q_frontc);
            Vector q_brf = QueryBase(q_rightc, q_bottomc, q_frontc);
            Vector q_tlb = QueryBase(q_leftc, q_topc, q_backc);
            Vector q_trb = QueryBase(q_rightc, q_topc, q_backc);
            Vector q_blb = QueryBase(q_leftc, q_bottomc, q_backc);
            Vector q_brb = QueryBase(q_rightc, q_bottomc, q_backc);

            float q_leftf = (float)q_leftc / (float)BASE_DATA_DIM;
            float q_rightf = (float)q_rightc / (float)BASE_DATA_DIM;
            float q_topf = (float)q_topc / (float)BASE_DATA_DIM;
            float q_bottomf = (float)q_bottomc / (float)BASE_DATA_DIM;
            float q_frontf = (float)q_frontc / (float)BASE_DATA_DIM;
            float q_backf = (float)q_backc / (float)BASE_DATA_DIM;
                                    
            float t_leftx = x - q_leftf;
            float t_rightx = t_leftx - (1f / (float)BASE_DATA_DIM);
            float t_topy = y - q_topf;
            float t_bottomy = t_topy - (1f / (float)BASE_DATA_DIM);
            float t_frontz = z - q_frontf;
            float t_backz = t_frontz - (1f / (float)BASE_DATA_DIM);

            float v_tlf = (q_tlf.x * t_leftx + q_tlf.y * t_topy + q_tlf.z * t_frontz) * (float)BASE_DATA_DIM;
            float v_trf = (q_trf.x * t_rightx + q_trf.y * t_topy + q_trf.z * t_frontz) * (float)BASE_DATA_DIM;
            float v_blf = (q_blf.x * t_leftx + q_blf.y * t_bottomy + q_blf.z * t_frontz) * (float)BASE_DATA_DIM;
            float v_brf = (q_brf.x * t_rightx + q_brf.y * t_bottomy + q_brf.z * t_frontz) * (float)BASE_DATA_DIM;
            float v_tlb = (q_tlb.x * t_leftx + q_tlb.y * t_topy + q_tlb.z * t_backz) * (float)BASE_DATA_DIM;
            float v_trb = (q_trb.x * t_rightx + q_trb.y * t_topy + q_trb.z * t_backz) * (float)BASE_DATA_DIM;
            float v_blb = (q_blb.x * t_leftx + q_blb.y * t_bottomy + q_blb.z * t_backz) * (float)BASE_DATA_DIM;
            float v_brb = (q_brb.x * t_rightx + q_brb.y * t_bottomy + q_brb.z * t_backz) * (float)BASE_DATA_DIM;

            float icX = InterpolateCurve(t_leftx * BASE_DATA_DIM);
            float icY = InterpolateCurve(t_topy * BASE_DATA_DIM);
            float icZ = InterpolateCurve(t_frontz * BASE_DATA_DIM);

            float vx0 = InterpolateLinear(v_tlf, v_trf, icX);
            float vx1 = InterpolateLinear(v_blf, v_brf, icX);
            float vx2 = InterpolateLinear(v_tlb, v_trb, icX);
            float vx3 = InterpolateLinear(v_blb, v_brb, icX);

            float vy0 = InterpolateLinear(vx0, vx1, icY);
            float vy1 = InterpolateLinear(vx2, vx3, icY);

            float vf = InterpolateLinear(vy0, vy1, icZ);

            return vf ;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (timer1.Enabled == false)
            {
                phase += (0.00005f * (float)e.Delta);
                GenerateNoise();
                Refresh();
            }
            base.OnMouseWheel(e);

        }


        void GenerateNoise()
        {
           // phase += 0.01f;
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            System.Drawing.Imaging.BitmapData bmpData =
                bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            IntPtr ptr = bmpData.Scan0;

            int bytes = bmpData.Stride * bmp.Height;
            byte[] rgbValues = new byte[bytes];

            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
                
            for (int counter = 0, i = 0; counter < rgbValues.Length; counter += 3, i++)
            { 

                float x = (float)(i % NOISE_DIM) / (float)NOISE_DIM;
                float y = (float)((int)(i / NOISE_DIM)) / (float)NOISE_DIM;

                float v = 1.0f;

                for (int o = 0, res = 1; o < octaves; o++, res *= 2)
                {
                    float amp = (float)Math.Pow(2.0, (double)o);
                    v += Noise(new Vector(x * res, y * res, phase), null) * (1f / amp);

                }
                
                v *= 0.5f;
                v = Math.Min(1f, Math.Max(0f, v));

                int vi = (int)(v * 255.0);
                Color c = Color.FromArgb(vi, vi, vi);

                rgbValues[counter + 0] = c.B;
                rgbValues[counter + 1] = c.G;
                rgbValues[counter + 2] = c.R;
            }

            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

            bmp.UnlockBits(bmpData);
        }

        Bitmap bmp = new Bitmap(NOISE_DIM, NOISE_DIM);
        float phase = 0f;
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);


            e.Graphics.DrawImage(bmp, 0, 0);
            ClientSize = new Size(NOISE_DIM, NOISE_DIM);

        }

        
        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        {
            if (octaves != 1)
            {
                octaves = 1;
                GenerateNoise();
                Refresh();
            }
        } 
        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            if (octaves != 2)
            {
                octaves = 2;
                GenerateNoise();
                Refresh();
            }
        }


        private void toolStripMenuItem4_Click(object sender, EventArgs e)
        {
            if (octaves != 3)
            {
                octaves = 3;
                GenerateNoise();
                Refresh();
            }
        }

        private void toolStripMenuItem5_Click(object sender, EventArgs e)
        {
            if (octaves != 4)
            {
                octaves = 4;
                GenerateNoise();
                Refresh();
            }
        }

        private void toolStripMenuItem6_Click(object sender, EventArgs e)
        {
            if (octaves != 5)
            {
                octaves = 5;
                GenerateNoise();
                Refresh();
            }

        } 

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (timer1.Enabled)
            {

                toolStripLabel1.Text = Math.Round(phase, 2).ToString();
                phase += 0.005f;

                GenerateNoise();
                Refresh();
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            if (timer1.Enabled)
            {
                timer1.Stop();
                toolStripButton1.Checked = false;
            }
            else
            {
                timer1.Start();
                toolStripButton1.Checked = true;
            }
        }

        
    }
}
